#pragma once
#include "../common/net.hpp"
#include <unordered_set>

namespace MyRpc
{
    namespace server
    {
        class TopicManage
        {
        public:
            using ptr = std::shared_ptr<TopicManage>;
            TopicManage() {}
            // 收到TopicRequest所调用的回调函数
            void OnRpcMessage(const BaseConnection::ptr& conn, const TopicRequest::ptr& msg)
            {
                // 根据请求的类型来进行对主题的操作
                TopicRequestOpType t_optype = msg->topicRequsetOpty();
                bool flag = true;
                switch(t_optype)
                {
                    // 添加主题
                    case TopicRequestOpType::TOPIC_CREATE : topicCreate(conn, msg); break;
                    // 删除主题
                    case TopicRequestOpType::TOPIC_REMOVE : topicRemove(conn, msg); break;
                    // 增加订阅者
                    case TopicRequestOpType::TOPIC_SUBSCRIBE : flag = subscribeAppend(conn, msg); break;
                    // 删除订阅者
                    case TopicRequestOpType::TOPIC_CANCEL : flag = subscribeDel(conn, msg); break;
                    // 对订阅者publish消息
                    case TopicRequestOpType::TOPIC_PUBLISH : subscribePublish(conn, msg); break;
                    default: return errorResponse(conn, msg, RCode::RCODE_INVALID_OPTYPE);
                }
                if(!flag)
                {
                    // 出错了
                    return errorResponse(conn, msg, RCode::RCODE_NOT_FOUND_TOPIC);
                }
                return okResponse(conn, msg);
            }
            void shutDownConntion(const BaseConnection::ptr& conn)
            {
                SubscribeDesc::ptr sub;
                std::unordered_set<std::string> topic;
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    // 判断是不是订阅者连接
                    auto it = _subscribe_manage.find(conn);
                    if(it == _subscribe_manage.end())
                    {
                        // 不是订阅者连接
                        return;
                    }
                    // 是订阅者连接 拿到这个连接
                    sub = it->second;
                    // 拿到这个订阅者订阅的所有主题
                    topic = sub->topics;
                    // 移除这个连接
                    _subscribe_manage.erase(conn);
                }
                // 移除这些主题中这个订阅者的连接
                for(auto& e : topic)
                {
                    auto conn_it = _topic_manage.find(e);
                    if(conn_it == _topic_manage.end())
                    {
                        // 没有这个主题
                        continue;
                    }
                    // 有这个主题 移除订阅者
                    conn_it->second->removeConn(sub);
                }
            }
        private:
            void okResponse(const BaseConnection::ptr& conn, const TopicRequest::ptr& msg)
            {
                auto rsp = MessageFactory::create(MType::RSP_TOPIC);
                auto rpc_rsp = std::dynamic_pointer_cast<TopicResponse>(rsp);
                rpc_rsp->setMType(MType::RSP_TOPIC);
                rpc_rsp->setRcode(RCode::RCODE_OK);
                rpc_rsp->setId(msg->id());
                conn->send(rsp);
            }
            void errorResponse(const BaseConnection::ptr& conn, const TopicRequest::ptr& msg, RCode rcode)
            {
                auto rsp = MessageFactory::create(MType::RSP_TOPIC);
                auto rpc_rsp = std::dynamic_pointer_cast<TopicResponse>(rsp);
                rpc_rsp->setMType(MType::RSP_TOPIC);
                rpc_rsp->setRcode(rcode);
                rpc_rsp->setId(msg->id());
                conn->send(rsp);
            }
        private:
            struct SubscribeDesc
            {
                using ptr = std::shared_ptr<SubscribeDesc>;
                std::mutex _mtx;
                std::unordered_set<std::string> topics;
                BaseConnection::ptr conn;
                SubscribeDesc(const BaseConnection::ptr& c)
                    :conn(c)
                {}
                void appendTopic(const std::string& topic)
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    topics.insert(topic);
                }
                void removeTopic(const std::string& topic)
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    topics.erase(topic);
                }
            };
            struct Topic
            {
                using ptr = std::shared_ptr<Topic>;
                std::mutex _mtx;
                std::string topic_name;
                std::unordered_set<SubscribeDesc::ptr> conns;
                Topic(const std::string& name)
                    :topic_name(name)
                {}
                void appendConn(const SubscribeDesc::ptr& sub)
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    conns.insert(sub);
                }
                void removeConn(const SubscribeDesc::ptr& sub)
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    conns.erase(sub);
                }
                void publishMessage(const BaseMessage::ptr msg)
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    for(auto& e : conns)
                    {
                        e->conn->send(msg);
                    }
                }
            };
        private:
            // 主题创建
            void topicCreate(const BaseConnection::ptr& conn, const TopicRequest::ptr& msg)
            {
                // 拿到主题名字信息
                Topic::ptr topic;
                std::string t_name = msg->key();
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    auto topic_it = _topic_manage.find(t_name);
                    if(topic_it == _topic_manage.end())
                    {
                        // 没有找到这个主题
                        topic = std::make_shared<Topic>(t_name);
                        _topic_manage.insert(std::make_pair(t_name, topic));
                    }
                }
            }
            // 删除主题
            void topicRemove(const BaseConnection::ptr& conn, const TopicRequest::ptr& msg)
            {
                // 拿到主题名字信息
                std::unordered_set<SubscribeDesc::ptr> sub_des;
                Topic::ptr topic;
                std::string t_name = msg->key();
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    auto topic_it = _topic_manage.find(t_name);
                    if(topic_it == _topic_manage.end())
                    {
                        // 没有找到这个主题
                        return;
                    }
                    else
                    {
                        // 找到了这个主题
                        topic = topic_it->second;
                    }
                    // 拿到所有的订阅主题的订阅者
                    sub_des = topic->conns;
                    // 删除当前的主题
                    _topic_manage.erase(t_name);
                }
                for(auto& e : sub_des)
                {
                    e->removeTopic(t_name);
                }
            }
            // 添加订阅者
            bool subscribeAppend(const BaseConnection::ptr& conn, const TopicRequest::ptr& msg)
            {
                Topic::ptr topic;
                SubscribeDesc::ptr sub_des;
                std::string t_name = msg->key();
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    // 先判断有没有这个主题
                    auto it = _topic_manage.find(t_name);
                    if(it == _topic_manage.end())
                    {
                        // 没有这个主题 无法订阅
                        return false;
                    }
                    // 走到这块说明有这个主题 
                    topic = it->second;
                    //判断有没有这个订阅者
                    auto conn_it = _subscribe_manage.find(conn);
                    if(conn_it == _subscribe_manage.end())
                    {
                        // 没有找到这个订阅者 创建一个订阅者
                        sub_des = std::make_shared<SubscribeDesc>(conn);
                        _subscribe_manage.insert(std::make_pair(conn, sub_des));
                    }
                    else
                    {
                        sub_des = conn_it->second;
                    }
                }
                topic->appendConn(sub_des);
                sub_des->appendTopic(t_name);
                return true;
            }
            // 删除订阅者 删除这个订阅者订阅的所有主题
            bool subscribeDel(const BaseConnection::ptr& conn, const TopicRequest::ptr& msg)
            {
                std::unordered_set<std::string> topic;
                SubscribeDesc::ptr sub;
                std::string t_name = msg->key();
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    //判断有没有这个订阅者
                    auto conn_it = _subscribe_manage.find(conn);
                    if(conn_it == _subscribe_manage.end())
                    {
                        // 没有这个订阅者
                        return false;
                    }
                    // 有这个订阅者 拿到这个订阅者订阅的所有主题信息
                    topic = conn_it->second->topics;
                    sub = conn_it->second;
                    // 删除这个订阅者
                    _subscribe_manage.erase(conn);
                }
                for(auto& e : topic)
                {
                    // 删除这个订阅者订阅的所有主题
                    auto it = _topic_manage.find(e);
                    if(it == _topic_manage.end())
                    {
                        // 没有找到这个订阅的主题
                        continue;
                    }
                    // 找到了就删除这个订阅者
                    it->second->removeConn(sub);
                }
                return true;
            }
            // 订阅发送消息
            void subscribePublish(const BaseConnection::ptr& conn, const TopicRequest::ptr& msg)
            {
                Topic::ptr topic;
                std::string t_name = msg->key();
                {
                    std::lock_guard<std::mutex> lock(_mtx);
                    // 先判断有没有这个主题
                    auto it = _topic_manage.find(t_name);
                    if(it == _topic_manage.end())
                    {
                        // 没有这个主题 无法进行publish信息
                        return;
                    }
                    topic = it->second;
                }
                topic->publishMessage(msg);
            }
        private:
            std::mutex _mtx;
            std::unordered_map<std::string, Topic::ptr> _topic_manage;
            std::unordered_map<BaseConnection::ptr, SubscribeDesc::ptr> _subscribe_manage;
        };
    }
}